package it.avantsoft.ls.gl.data;

import it.avantsoft.ls.base.Configuration;
import it.avantsoft.ls.base.Protocol;
import it.avantsoft.ls.gl.GroupChildHandler;
import it.avantsoft.ls.util.SimpleEntry;
import org.apache.log4j.Logger;

import java.util.Map;
import java.util.Set;

public class AddressBookManager implements Protocol {
    private static Logger log = Logger.getLogger(AddressBookManager.class);

    private UpperAB upperAB;
    private CenterAB centerAB;
    private LowerAB lowerAB;
    private ListAllowedPeer listAllowedPeer;
    private ListAllowedPeer incomingAllowedPeer;
    protected Object sync = null;
    private ABProtocol state;
    private int level;
    private SimpleEntry worstLowerPeer;

    public AddressBookManager() {
        upperAB = new UpperAB();
        centerAB = new CenterAB();
        lowerAB = new LowerAB();
        listAllowedPeer = new ListAllowedPeer();
        incomingAllowedPeer = new ListAllowedPeer();
        sync = new Object();
        state = ABProtocol.INITIALIZED;
    }

    private boolean acceptNewPeer() {
        int max = Configuration.MAX_GROUP_CHILD_ALLOWED;
        boolean accepted = lowerAB.size() < max;
        if (!accepted) {
            log.info("max allowed child number (" + max + ") excedeed -> " + (max + 1));
        }
        return accepted;
    }

    public boolean addAllowedPeer(String childId) {
        log.info("Adding allowed child " + childId);
        return listAllowedPeer.addAllowedPeer(childId);

    }

    public boolean addIncomingAllowedChild(String childId) {
        log.info("Adding incoming child " + childId);
        if (acceptNewPeer()) {
            log.info("childId " + childId + " added to AllowedPeer");
            return incomingAllowedPeer.addAllowedPeer(childId);
        } else {
            return false;
        }
    }

    public boolean removeAllowedPeer(String childId) {
        log.info("removing allowed peer " + childId);
        return listAllowedPeer.removeAllowedPeer(childId);
    }

    private boolean removeIncomingAllowedPeer(String childId) {
        log.info("removing incoming peer " + childId);
        return incomingAllowedPeer.removeAllowedPeer(childId);
    }

    public boolean isAllowed(String childId) {
        return listAllowedPeer.isAllowed(childId);
    }

    public boolean isIncoming(String childId) {
        return incomingAllowedPeer.isAllowed(childId);
    }

    public Set<String> getAllowedPeerList() {
        return listAllowedPeer.getSet();
    }

    public Set<String> getIncomingAllowedPeerList() {
        return incomingAllowedPeer.getSet();
    }

    public boolean addLowerPeer(GroupChildHandler child, boolean checkAllowedPeer) {
        boolean added = false;
        boolean canAdd;
        if (checkAllowedPeer) {
            canAdd = removeIncomingAllowedPeer(child.getChildId());
        } else {
            canAdd = acceptNewPeer();
        }
        if (canAdd) {
            synchronized (sync) {
                added = lowerAB.add(child);
                sync.notify();
                log.info("child added to LowerAB -> GroupLeader Notified");
            }
        } else {
            log.info("error inserting Child to LowerAB");
        }
        return added;
    }

    public boolean removeLowerPeer(String childId) {
        return lowerAB.remove(childId);
    }

    public boolean addUpperPeer(String host, int port) {
        if (upperAB.add(host, port)) {
            return true;
        } else {
            log.info("Error Inserting Child to UpperAB");
            return false;
        }
    }

    public boolean removeUpperPeer(String childId) {
        return upperAB.remove(childId);
    }

    public boolean addCentralPeer(String host, int port) {
        if (centerAB.add(host, port)) {
            return true;
        } else {
            log.info("Error Inserting Child to CenterAB (String,int)");
            return false;
        }
    }

    public boolean addCentralPeer(String id) {
        if (centerAB.add(id)) {
            return true;
        } else {
            log.info("Error Inserting Child to CenterAB (String)");
            return false;
        }
    }

    public boolean removeCentralPeer(String childId) {
        return centerAB.remove(childId);
    }

    public GroupChildHandler getBestUpperPeer() {
        return upperAB.getBestPeer();
    }

    public GroupChildHandler getBestCentralPeer() {
        return centerAB.getBestPeer();
    }

    public GroupChildHandler getBestLowerPeer() {
        return lowerAB.getBestPeer();
    }

    public UpperAB getUpperAB() {
        return upperAB;
    }

    public CenterAB getCenterAB() {
        return centerAB;
    }

    public LowerAB getLowerAB() {
        return lowerAB;
    }

    public byte[] toByte(int addressBookType) {
        byte[] byteArray = null;
        switch (addressBookType) {

            case 0: //UpperAddressBook
                byteArray = upperAB.toByte();
                break;
            case 1://CentralAddressBook
                byteArray = centerAB.toByte();
                break;
            case 2://LowerAddressBook
                byteArray = lowerAB.toByte();
                break;
        }

        return byteArray;
    }

    public boolean fromByte(byte[] byteArray) {
        boolean reconstruction = false;
        byte addressBookType = byteArray[0];

        switch (addressBookType) {
            case 0: //UpperAddressBook
                reconstruction = upperAB.fromByte(byteArray);
            case 1: //CentralAddressBook
                reconstruction = centerAB.fromByte(byteArray);
            case 2: //UpperAddressBook
                reconstruction = lowerAB.fromByte(byteArray);

        }

        return reconstruction;
    }

    public static AddressBook getAddressBook(int addressBookID) {
        AddressBook addressBook = null;

        switch (addressBookID) {

            case AddressBook.UPPERAB_ID:
                addressBook = new UpperAB();
                break;
            case AddressBook.CENTERAB_ID:
                addressBook = new CenterAB();
                break;
            case AddressBook.LOWERAB_ID:
                addressBook = new LowerAB();
                break;
        }
        return addressBook;
    }

    public boolean newAB(AddressBook addressBook) {
        boolean merged = false;
/*        switch (newAddressBook.getAbID()) {
            case AddressBook.UPPERAB_ID:
                merged = upperAB.mergeAB(newAddressBook);
                break;
            case AddressBook.CENTERAB_ID:
                merged = centerAB.mergeAB(newAddressBook);
                break;
            case AddressBook.LOWERAB_ID:
                merged = lowerAB.mergeAB(newAddressBook);
                break;
        }*/
        if ((addressBook instanceof LowerAB))
            merged = lowerAB.mergeAB(addressBook);
        if ((addressBook instanceof CenterAB)) {
            merged = centerAB.mergeAB(addressBook);
        }
        if ((addressBook instanceof UpperAB)) {
            merged = upperAB.mergeAB(addressBook);
        }
        return merged;
    }

    public GroupChildHandler getChildHandler(String childId) {
        GroupChildHandler groupChildHandler = null;
        if (upperAB.hasPeerEntry(childId))
            groupChildHandler = upperAB.getChildHandler(childId);
        if (centerAB.hasPeerEntry(childId))
            groupChildHandler = centerAB.getChildHandler(childId);
        if (lowerAB.hasPeerEntry(childId))
            groupChildHandler = lowerAB.getChildHandler(childId);
        return groupChildHandler;
    }


    public boolean newAppraisals(Appraisals appraisals) {
        ABProtocol newState;
        upperAB.updateAppraisals(appraisals);
        centerAB.updateAppraisals(appraisals);
        newState = lowerAB.updateAppraisals(appraisals);
        synchronized (sync) {
            setState(newState);
            sync.notify();
            log.info("ABManager new state signaled -> " + state);
        }
        return true;
    }

    public ABProtocol getState() {
        return state;
    }

    public void setState(ABProtocol state) {
        this.state = state;
    }

    public Object getSync() {
        return sync;
    }

    public GroupChildHandler getMostLoadedPeer() {
        return lowerAB.getMostLoadedPeer();
    }

    public GroupChildHandler getLessLoadedPeer() {
        return lowerAB.getLessLoadedPeer();
    }

    public int getLoadDifference() {
        return lowerAB.getLoadDifference();
    }

    public int getLevel() {
        level = lowerAB.getLevel();
        return level;
    }

    public boolean addUpperPeer(String id) {
        return upperAB.add(id);
    }


    public boolean peerKnown(String childId) {
        boolean known = false;
        if (lowerAB.peerKnown(childId) || centerAB.peerKnown(childId) || upperAB.peerKnown(childId))
            known = true;
        return known;
    }

    public Map.Entry<String, Integer> getNextGL() {
        Map.Entry<String, Integer> gl = upperAB.getGL();
        log.info("Looking for a new GroupLeader: " + gl);
        return gl;
    }


    public SimpleEntry getWorstLowerPeer() {
        worstLowerPeer = lowerAB.getWorstPeer();
        return worstLowerPeer;
    }
}
